<?php
/**
 * Actions modify the AMF message PER BODY
 * This allows batching of calls
 * 
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 * @copyright (c) 2003 amfphp.org
 * @package flashservices
 * @subpackage filters
 * @version $Id: Filters.php,v 1.6 2005/04/02   18:37:51 pmineault Exp $
 */

include_once(AMFPHP_BASE . 'util/Authenticate.php');

/**
 * Catches any special request types and classifies as required
 */
function adapterAction (&$amfbody) {
    $baseClassPath = $GLOBALS['amfphp']['classPath'];

    $uriclasspath = "";
    $classname = "";
    $classpath = "";
    $methodname = "";
    $isWebServiceURI = false;

    $target = $amfbody->targetURI;
    
    if (strpos($target, "http://") === false && strpos($target, "https://") === false) { // check for a http link which means web service
        $lpos = strrpos($target, ".");
        if ($lpos === false) {
            // throw an error because there has to be atleast 1
            trigger_error('Malformed target: ' . $target, E_USER_ERROR);
            return false;
        } else {
            $methodname = substr($target, $lpos + 1);
        } 
        $trunced = substr($target, 0, $lpos);
        $lpos = strrpos($trunced, ".");
        if ($lpos === false) {
            $classname = $trunced;
            if ($classname == "PageAbleResult" && $methodname == 'getRecords') {
                $val = $amfbody->getValue();
                $id = $val[0];
                $keys = explode("=", $id);
                $currset = intval($keys[1]);
                
                $set = $_SESSION['amfphp_recordsets'][$currset];
                
                $uriclasspath = $set['class'];
                $classpath = $baseClassPath . $set['class'];
                $methodname = $set['method'];
                
                $classname = substr(strrchr('/' . $set['class'], '/'), 1, -4);
                
                //Now set args for body
                $amfbody->setValue(array_merge($set['args'], array($val[1], $val[2])));
                
                //Tell amfbody that this is a dynamic paged resultset
                $amfbody->setSpecialHandling('pageFetch');
            } 
            else if($classname == "PageAbleResult" && $methodname == 'release')
            {
                $amfbody->setSpecialHandling('pageRelease');
                $amfbody->noExec = true;
            }
            else {
                $uriclasspath = $trunced . ".php";
                $classpath = $baseClassPath . $trunced . ".php";
            } 
        } else {
            $classname = substr($trunced, $lpos + 1);
            $classpath = $baseClassPath . str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here
            $uriclasspath = str_replace(".", "/", $trunced) . ".php"; // removed to strip the basecp out of the equation here
        } 
    } else { // launch a web service and not a php service
        $amfbody->setSpecialHandling('ws');
        $amfbody->noExec = true;
        $rdot = strrpos($target, ".");
        $classpath = substr($target, 0, $rdot);
        $methodname = substr($target, $rdot + 1);
    } 

    $amfbody->classPath = $classpath;
    $amfbody->uriClassPath = $uriclasspath;
    $amfbody->className = $classname;
    $amfbody->methodName = $methodname;

    return true;
} 

/**
 * Class loader action loads the class from which we will get the remote method
 */
function classLoaderAction (&$amfbody) {
     
    if(!$amfbody->noExec)
    { 
        // change to the gateway.php script directory
        // now change to the directory of the classpath.  Possible relative to gateway.php
        $dirname = dirname($amfbody->classPath); 
        if(is_dir($dirname))
        {
            chdir($dirname);
        }
        else
        {
            $ex = new AMFException(E_USER_ERROR, "The classpath folder {" . $amfbody->classPath . "} does not exist. You probably misplaced your service." , __FILE__, __LINE__, "AMFPHP_CLASSPATH_NOT_FOUND");
            AMFException::throwException($amfbody, $ex);
            return false;
        }
       
        $fileExists = @file_exists(basename($amfbody->classPath)); // see if the file exists
        if(!$fileExists)
        {
                $ex = new AMFException(E_USER_ERROR, "The class {" . $amfbody->className . "} could not be found under the class path {" . $amfbody->classPath . "}" , __FILE__, __LINE__, "AMFPHP_FILE_NOT_FOUND");
                AMFException::throwException($amfbody, $ex);
                return false;
        }
        
        $fileIncluded = Executive::includeClass($amfbody, "./" . basename($amfbody->classPath));
    
        if (!$fileIncluded) 
        { 
            $ex = new AMFException(E_USER_ERROR, "The class file {" . $amfbody->className . "} exists but could not be included. The file may have syntax errors, or includes at the top of the file cannot be resolved.", __FILE__, __LINE__, "AMFPHP_FILE_NOT_INCLUDED");
            AMFException::throwException($amfbody, $ex);
            return false;
        }
        
        if (!class_exists($amfbody->className))
        { // Just make sure the class name is the same as the file name
                $ex = new AMFException(E_USER_ERROR, "The file {" . $amfbody->className . ".php} exists and was included correctly but a class by that name could not be found in that file. Perhaps the class is misnamed.", __FILE__, __LINE__, "AMFPHP_CLASS_NOT_FOUND");
                AMFException::throwException($amfbody, $ex);
                return false;
        }

        //Let executive handle building the class
        //The executive can handle making exceptions and all that, that's why
        $classConstruct = Executive::buildClass($amfbody, $amfbody->className);

        if($classConstruct !== '__amfphp_error')
        {
            $amfbody->setClassConstruct($classConstruct);
        }
        else
        {
            return false;
        }
    }
    return true;
} 

/**
 * MetaDataAction loads the required info from the methodTable
 */
function metadataAction (&$amfbody) {
    if(!$amfbody->noExec)
    {
        $classConstruct = &$amfbody->getClassConstruct();
        $methodName = $amfbody->methodName;
        $className = $amfbody->className;
        
        if($methodName !== '_authenticate')
        {
            if (!isset($classConstruct->methodTable)) { // check to see if the methodTable exists
                $ex = new AMFException(E_USER_ERROR, "This class has no methodTable, therefore it cannot be run. Are you using include_once('" . $className . ".methodTable.php') instead of include (without the _once)? This would make the second call to the class fail.", __FILE__, __LINE__, "AMFPHP_NO_METHOD_TABLE");
                AMFException::throwException($amfbody, $ex);
                return false;
            } 
            if (!isset($classConstruct->methodTable[$methodName])) { // check to see if the methodTable exists
                $ex = new AMFException(E_USER_ERROR, "The method  {" . $methodName . "} was not declared in the meta data for class {" . $className . "}.", __FILE__, __LINE__, "AMFPHP_UNDECLARED_METHOD");
                AMFException::throwException($amfbody, $ex);
                return false;
            } 
            
            //Check if there is an alias
            if(isset($classConstruct->methodTable[$methodName]['alias']))
            {
                $alias = $classConstruct->methodTable[$methodName]['alias'];
                $amfbody->methodName = $alias;
                $methodName = $alias;
            }
        }
        
        //Check if method exists
        if (!method_exists($classConstruct, $methodName)) { // check to see if the method exists
            $ex = new AMFException(E_USER_ERROR, "The method  {" . $methodName . "} does not exist in class {" . $className . "}.", __FILE__, __LINE__, "AMFPHP_INEXISTANT_METHOD");
            AMFException::throwException($amfbody, $ex);
            return false;
        } 
    }
    return true;
}

/**
 * Security action checks that the caller has the credentials to run the remote methods
 */
function securityAction (&$amfbody) {
    $check = true;
    if(!$amfbody->noExec)
    {
        $classConstruct = &$amfbody->getClassConstruct();
        $methodName = $amfbody->methodName;
        $className = $amfbody->className;
        
        if ($methodName == "_authenticate") {
            if (method_exists($classConstruct, "_authenticate")) {
                $credentials = $amfbody->getValue();
                
                //Fix for error in _authenticate
                //Pass throught the executive
                $roles = Executive::doMethodCall($amfbody, 
                                            $classConstruct, 
                                            '_authenticate', 
                                            array($credentials['userid'], 
                                                  $credentials['password']));
                if ($roles !== '__amfphp_error' && $roles !== false && $roles !== "") {
                    Authenticate::login($credentials['userid'], $roles);
                    return false;
                } else {
                    Authenticate::logout();
                    return false;
                } 
            } else {
                $ex = new AMFException(E_USER_ERROR, "The _authenticate method was not found in the " . $className . " class", __FILE__, __LINE__, "AMFPHP_AUTHENTICATE_NOT_FOUND");
                AMFException::throwException($amfbody, $ex);
                return false;
            } 
        } 
        
        //else
        //Check for gateway restrictions
        $methodRecord = $classConstruct->methodTable[$methodName]; // create a shortcut for the ugly path
        $instanceName = $GLOBALS['amfphp']['instanceName'];
        if (isset($instanceName) && isset($methodRecord['instance'])) { // see if we have an instance defined
            if ( $instanceName != $methodRecord['instance']) { // if the names don't match die
                $ex = new AMFException(E_USER_ERROR, "The method {" . $methodName . "} instance name does not match this gateway's instance name.", __FILE__, __LINE__, "AMFPHP_INSTANCE_NAME_MISMATCH");
                AMFException::throwException($amfbody, $ex);
                return false;
            } 
        } else if (isset($methodRecord['instance'])) { // see if the method has an instance defined
            if ($instanceName != $methodRecord['instance']) { // if the names don't match die
                $ex = new AMFException(E_USER_ERROR, "The restricted method {" . $methodName . "} is not allowed through a non-restricted gateway.", __FILE__, __LINE__, "AMFPHP_INSTANCE_NAME_RESTRICTION");
                AMFException::throwException($amfbody, $ex);
                return false;
            } 
        } 
        
        if (!isset($methodRecord['access']) || (strtolower($methodRecord['access']) != "remote")) { // make sure we can remotely call it
            $ex = new AMFException(E_USER_ERROR, "ACCESS DENIED: The method {" . $methodName . "} has not been declared a remote method.", __FILE__, __LINE__, "AMFPHP_METHOD_NOT_REMOTE");
            AMFException::throwException($amfbody, $ex);
            return false;
        } 
        
        if (isset($methodRecord['roles']) && !Authenticate::isUserInRole($methodRecord['roles'])) {
                $ex = new AMFException(E_USER_ERROR, "This user is not does not have access to {" . $methodName . "}.", __FILE__, __LINE__, "AMFPHP_AUTH_MISMATCH");
                AMFException::throwException($amfbody, $ex);
                return false;
        } 
    }
    return true;
} 

/**
 * ExecutionAction executes the required methods
 */
function executionAction (&$amfbody) 
{
    $specialHandling = $amfbody->getSpecialHandling();

    if (!$amfbody->isSpecialHandling() || $amfbody->isSpecialHandling(array('describeService', 'pageFetch')))
    {
        $construct = &$amfbody->getClassConstruct();
        $method = $amfbody->methodName;
        $args = $amfbody->getValue();
        
        if(isset($construct->methodTable[$method]['fastArray']) &&
           $construct->methodTable[$method]['fastArray'] == true)
        {
            $amfbody->setMetaData('fastArray', true);
        }
        
        if($specialHandling == 'describeService')
        {               
            include_once(AMFPHP_BASE . "util/DescribeService.php");
            $ds = new DescribeService();
            $results = $ds->describe($construct, $amfbody->className);
        }
        else if($specialHandling == 'pageFetch')
        {
            $args[count($args) - 2] = $args[count($args) - 2] - 1;
            
            $dataset = Executive::doMethodCall($amfbody, $construct, $method, $args);
            $results = array("cursor" => $args[count($args) - 2] + 1,
                             "data" => $dataset);
            $amfbody->setMetadata('type', '__DYNAMIC_PAGE__');
        }
        else
        {
            if(isset($construct->methodTable[$method]['pagesize']))
            {
                //Check if counting method was overriden
                if(isset($construct->methodTable[$method]['countMethod']))
                {
                    $counter = $construct->methodTable[$method]['countMethod'];
                }
                else
                {
                    $counter = $method . '_count';
                }
                
                $dataset = Executive::doMethodCall($amfbody, $construct, $method, $args); // do the magic
                $count = Executive::doMethodCall($amfbody, $construct, $counter, $args);
                
                //Include the wrapper
                $results = array('class' => $amfbody->uriClassPath, 
                                 'method' => $amfbody->methodName, 
                                 'count' => $count, 
                                 "args" => $args, 
                                 "data" => $dataset);
                $amfbody->setMetadata('type', '__DYNAMIC_PAGEABLE_RESULTSET__');
                $amfbody->setMetadata('pagesize', $construct->methodTable[$method]['pagesize']);
            }
            else
            {
                //The usual
                $results = Executive::doMethodCall($amfbody, $construct, $method, $args); // do the magic
            }
        }

        if($results !== '__amfphp_error')
        {
            $amfbody->setResults($results);
            if(isset($construct->methodTable[$method]['returns'])
                && !isset($construct->methodTable[$method]['pagesize']))
            {
                $amfbody->setMetadata('type', $construct->methodTable[$method]['returns']);
            }
            $amfbody->responseURI = $amfbody->responseIndex . "/onResult";  
        }
        return false;
    }
    else if($specialHandling == 'pageRelease')
    {
        //Ignore PageAbleResult.release
        $amfbody->setResults(true);
        $amfbody->setMetaData('type', 'boolean');
        $amfbody->responseURI = $amfbody->responseIndex . "/onResult";
        return false;
    }
    return true;
}

/**
 * WebServiceAction calls a remote webservice instead of a regular method
 */
function webServiceAction (&$amfbody) {
    
    $method = $GLOBALS['amfphp']['webServiceMethod'];
    
    if ($amfbody->getSpecialHandling() == 'ws') {
        $args = &$amfbody->getValue();
        $webServiceURI = $amfbody->classPath;
        $webServiceMethod = $amfbody->methodName;
        $phpInternalEncoding = CharsetHandler::getPhpCharset();
        
        $functionName = "webServiceAction_$method";
        
        //Include web service actions
        include_once(AMFPHP_BASE . "app/WebServiceActions.php");
        $results = $functionName($amfbody, $webServiceURI, $webServiceMethod, $args, $phpInternalEncoding);
        if($results != '__amfphp_error')
        {
            $amfbody->setResults($results);
            $amfbody->responseURI = $amfbody->responseIndex . "/onResult";
        }
    }
    return false;
} 
?>